package com.tonyodev.fetch2.fetch;

import ohos.eventhandler.EventHandler;
import ohos.utils.Pair;

import com.tonyodev.fetch2.CompletedDownload;
import com.tonyodev.fetch2.Download;
import com.tonyodev.fetch2.Error;
import com.tonyodev.fetch2.Fetch;
import com.tonyodev.fetch2.FetchConfiguration;
import com.tonyodev.fetch2.FetchErrorUtils;
import com.tonyodev.fetch2.FetchGroup;
import com.tonyodev.fetch2.FetchListener;
import com.tonyodev.fetch2.NetworkType;
import com.tonyodev.fetch2.Request;
import com.tonyodev.fetch2.Status;
import com.tonyodev.fetch2.database.DownloadInfo;
import com.tonyodev.fetch2.database.FetchDatabaseManagerWrapper;
import com.tonyodev.fetch2.exception.FetchException;
import com.tonyodev.fetch2.util.ActiveDownloadInfo;
import com.tonyodev.fetch2.util.Defaults;
import com.tonyodev.fetch2.util.FetchUtils;
import com.tonyodev.fetch2.util.TypeConverterExtensions;
import com.tonyodev.fetch2core.DownloadBlock;
import com.tonyodev.fetch2core.Downloader;
import com.tonyodev.fetch2core.Extras;
import com.tonyodev.fetch2core.FetchErrorStrings;
import com.tonyodev.fetch2core.FetchObserver;
import com.tonyodev.fetch2core.FileResource;
import com.tonyodev.fetch2core.Func;
import com.tonyodev.fetch2core.Func2;
import com.tonyodev.fetch2core.HandlerWrapper;
import com.tonyodev.fetch2core.Logger;
import com.tonyodev.fetch2core.Reason;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static com.tonyodev.fetch2.FetchErrorUtils.getErrorFromMessage;

/**
 * @since 2021-05-20
 */
public class FetchImpl implements Fetch {
    private String namespace;
    private FetchConfiguration fetchConfiguration;
    private HandlerWrapper handlerWrapper;
    private EventHandler uiHandler;
    private FetchHandler fetchHandler;
    private Logger logger;
    private ListenerCoordinator listenerCoordinator;
    private FetchDatabaseManagerWrapper fetchDatabaseManagerWrapper;
    private Object lock = new Object();

    private Boolean closed = false;
    private LinkedHashSet<ActiveDownloadInfo> activeDownloadsSet = new LinkedHashSet<>();

    public FetchImpl(String namespace,
                     FetchConfiguration fetchConfiguration,
                     HandlerWrapper handlerWrapper,
                     EventHandler uiHandler,
                     FetchHandler fetchHandler,
                     Logger logger,
                     ListenerCoordinator listenerCoordinator, FetchDatabaseManagerWrapper fetchDatabaseManagerWrapper) {
        this.namespace = namespace;
        this.fetchConfiguration = fetchConfiguration;
        this.handlerWrapper = handlerWrapper;
        this.uiHandler = uiHandler;
        this.fetchHandler = fetchHandler;
        this.logger = logger;
        this.listenerCoordinator = listenerCoordinator;
        this.fetchDatabaseManagerWrapper = fetchDatabaseManagerWrapper;
        handlerWrapper.post(new Runnable() {
            @Override
            public void run() {
                fetchHandler.init();
            }
        });
        registerActiveDownloadsRunnable();
    }

    private void registerActiveDownloadsRunnable() {
        handlerWrapper.postDelayed(activeDownloadsRunnable, fetchConfiguration.getActiveDownloadsCheckInterval());
    }

    @Override
    public boolean isClosed() {
        synchronized (lock) {
            return closed;
        }
    }

    private Runnable activeDownloadsRunnable = new Runnable() {
        @Override
        public void run() {
            if (!isClosed()) {
                boolean hasActiveDownloadsAdded = fetchHandler.hasActiveDownloads(true);
                boolean hasActiveDownloads = fetchHandler.hasActiveDownloads(false);
                uiHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        if (!isClosed()) {
                            Iterator<ActiveDownloadInfo> iterator = activeDownloadsSet.iterator();
                            ActiveDownloadInfo activeDownloadInfo;
                            Boolean hasActive;
                            while (iterator.hasNext()) {
                                activeDownloadInfo = iterator.next();
                                if (activeDownloadInfo.getIncludeAddedDownloads()) {
                                    hasActive = hasActiveDownloadsAdded;
                                } else {
                                    hasActive = hasActiveDownloads;
                                }
                                activeDownloadInfo.getFetchObserver().onChanged(hasActive, Reason.REPORTING);

                            }
                        }
                    }
                });
            }
        }
    };

    @Override
    public String getNamespace() {
        return null;
    }

    @Override
    public FetchConfiguration getFetchConfiguration() {
        return null;
    }

    @Override
    public Fetch enqueue(Request request, Func<Request> func, Func<Error> func2) throws FetchException {
        //  result -> 等价于 new Func<List<Pair<Request, Error>>>()
        enqueueRequest(Collections.singletonList(request), result -> {
            if (!result.isEmpty()) {
                Pair<Request, Error> enqueuedPair = result.get(0);
                if (enqueuedPair.s != Error.NONE) {
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func2 !=null){

                                func2.call(enqueuedPair.s);
                            }
                        }
                    });

                } else {
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(enqueuedPair.f);
                            }
                        }
                    });

                }
            } else {
                uiHandler.postTask(new Runnable() {
                    @Override
                    public void run() {
                        if (func2 !=null){

                            func2.call(Error.ENQUEUE_NOT_SUCCESSFUL);
                        }
                    }
                });

            }
        }, func2);
        return this;
    }

    @Override
    public Fetch enqueue(List<Request> requests, Func<List<Pair<Request, Error>>> func) {
        enqueueRequest(requests, func, null);
        return this;
    }

    private void enqueueRequest(List<Request> requests, Func<List<Pair<Request, Error>>> func, Func<Error> func2) {

        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        HashSet<String> hashSet = new HashSet<>();
                        List<Request> requests1 = new ArrayList<>();
                        for (Request request : requests) {
                            if (hashSet.add(request.getFile())) {
                                requests1.add(request);
                            }
                        }
                        int distinctCount = requests1.size();
                        //int distinctCount = requests.distinctBy { it.file }.count() 翻译比较难记录,以上代码
                        if (distinctCount != requests.size()) {
                            throw new FetchException(FetchErrorStrings.ENQUEUED_REQUESTS_ARE_NOT_DISTINCT);
                        }
                        List<Pair<Download, Error>> downloadPairs = fetchHandler.enqueue(requests);

                        for (Pair<Download, Error> downloadPair : downloadPairs) {
                            Download download = downloadPair.f;

                            switch (download.getStatus()) {
                                case ADDED:
                                    listenerCoordinator.mainListener.onAdded(download);
                                    break;
                                case QUEUED:
                                    DownloadInfo downloadCopy = TypeConverterExtensions.toDownloadInfo(download, fetchDatabaseManagerWrapper.getNewDownloadInfoInstance());
                                    downloadCopy.setStatus(Status.ADDED);
                                    listenerCoordinator.mainListener.onAdded(downloadCopy);
                                    listenerCoordinator.mainListener.onQueued(download, false);
                                    break;
                                case COMPLETED:
                                    listenerCoordinator.mainListener.onCompleted(download);
                                    break;
                                default:
                                    break;
                            }
                        }
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                List<Pair<Request, Error>> newList = new ArrayList<>(10);
                                for (Pair<Download, Error> downloadPair : downloadPairs) {
                                    Pair<Request, Error> pair = new Pair<>(downloadPair.f.getRequest(), downloadPair.s);
                                    newList.add(pair);
                                }
                                if (func !=null) {
                                    func.call(newList);
                                }
                            }
                        });


                    } catch (Exception e) {
                        logger.e("Failed to enqueue list $requests");
                        Error error = FetchErrorUtils.getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });

                        }
                    }

                }
            });
        }
    }

    private void throwExceptionIfClosed() {
        if (closed) {
            throw new FetchException("This fetch instance has been closed. Create a new " +
                "instance using the builder.");
        }
    }

    @Override
    public Fetch pause(List<Integer> ids, Func<List<Download>> func, Func<Error> func2) {

        pauseDownloads(ids, null, func, func2);
        return this;
    }

    @Override
    public Fetch pause(List<Integer> ids) {
        return pause(ids, null, null);
    }

    @Override
    public Fetch pause(int id, Func<Download> func, Func<Error> func2) {

        return pause(Collections.singletonList(id), new Func<List<Download>>() {
            @Override
            public void call(List<Download> result) {
                if (result.isEmpty()){
                    if (func2 !=null){

                        func2.call(Error.REQUEST_DOES_NOT_EXIST);
                    }
                }else {
                    if (func !=null) {
                        func.call(result.get(0));
                    }
                }

            }
        }, func2);

    }

    @Override
    public Fetch pause(int id) {
        return pause(id, null, null);
    }

    private void pauseDownloads(List<Integer> ids, Integer groupId, Func<List<Download>> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<Download> downloads = null;
                        if (ids != null) {
                            downloads = fetchHandler.pause(ids);
                        } else {
                            if (groupId != null) {
                                downloads = fetchHandler.pausedGroup(groupId);
                            } else {
                                downloads = Collections.emptyList();
                            }
                        }

                        for (Download download : downloads) {
                            listenerCoordinator.mainListener.onPaused(download);
                        }
                        List<Download> finalDownloads = downloads;
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func !=null) {

                                    func.call(finalDownloads);
                                }
                            }
                        });
                    } catch (Exception e) {
                        logger.e("Fetch with namespace $namespace error", e);
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {

                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
        }
    }

    @Override
    public Fetch pauseGroup(int id, Func<List<Download>> func, Func<Error> func2) {
        pauseDownloads(null, id, func, func2);
        return this;
    }

    @Override
    public Fetch pauseGroup(int id) {
        return pauseGroup(id, null, null);
    }

    @Override
    public Fetch freeze(Func<Boolean> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        fetchHandler.freeze();
                        if (func != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func.call(true);
                                }
                            });
                        }
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }

    }

    @Override
    public Fetch pauseAll() {

        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<Download> downloads = fetchHandler.pauseAll();
                        for (Download download : downloads) {
                            listenerCoordinator.mainListener.onPaused(download);
                        }
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                    }
                }
            });
            return this;
        }

    }

    @Override
    public Fetch freeze() {
        return freeze(null, null);
    }

    private void resumeDownloads(List<Integer> ids, int groupId, Func<List<Download>> func, Func<Error> func2) {

        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<Download> downloads = null;
                        if (ids != null) {
                            downloads = fetchHandler.resume(ids);
                        } else {
                            if (groupId != 0) {
                                downloads = fetchHandler.resumeGroup(groupId);
                            } else {
                                downloads = Collections.emptyList(); //空的list
                            }
                        }
                        for (Download download : downloads) {
                            listenerCoordinator.mainListener.onQueued(download, false);
                            listenerCoordinator.mainListener.onResumed(download);
                        }

                        List<Download> finalDownloads = downloads;
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func !=null) {

                                    func.call(finalDownloads);
                                }
                            }
                        });
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
        }
    }

    @Override
    public Fetch resume(List<Integer> ids, Func<List<Download>> func, Func<Error> func2) {
        resumeDownloads(ids, 0, func, func2);
        return this;
    }

    @Override
    public Fetch resume(List<Integer> ids) {

        return resume(ids, null, null);
    }

    @Override
    public Fetch resume(int id, Func<Download> func, Func<Error> func2) {
        return resume(Collections.singletonList(id), new Func<List<Download>>() {
            @Override
            public void call(List<Download> result) {
                    if (result.isEmpty()){
                        if (func2 !=null) {

                            func2.call(Error.REQUEST_DOES_NOT_EXIST);
                        }
                    }else {
                        if (func !=null) {
                            func.call(result.get(0));
                        }
                    }

            }
        }, func2);
    }

    @Override
    public Fetch resume(int id) {
        return resume(id, null, null);
    }

    @Override
    public Fetch resumeGroup(int id, Func<List<Download>> func, Func<Error> func2) {
        resumeDownloads(null, id, func, func2);
        return this;
    }

    @Override
    public Fetch resumeAll() {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<Download> downloads = fetchHandler.resumeAll();
                        for (Download download : downloads) {
                            listenerCoordinator.mainListener.onQueued(download, false);
                            listenerCoordinator.mainListener.onResumed(download);
                        }
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                    }
                }
            });
        }
        return this;
    }

    @Override
    public Fetch resumeGroup(int id) {
        return resumeGroup(id, null, null);
    }

    @Override
    public Fetch unfreeze(Func<Boolean> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        fetchHandler.unfreeze();
                        if (func != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func.call(true);
                                }
                            });
                        }
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });

            return this;
        }
    }

    @Override
    public Fetch unfreeze() {
        return unfreeze(null, null);
    }


    public interface IDownloadAction {
        List<Download> invoke();
    }

    private Fetch executeRemoveAction(IDownloadAction IDownloadAction, Func<List<Download>> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<Download> downloads = IDownloadAction.invoke();
                        for (Download download : downloads) {
                            listenerCoordinator.mainListener.onRemoved(download);
                        }
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func !=null) {

                                    func.call(downloads);
                                }
                            }
                        });
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }

    }
    @Override
    public Fetch remove(List<Integer> ids, Func<List<Download>> func, Func<Error> func2) {

        return executeRemoveAction(() -> fetchHandler.remove(ids), func, func2);
    }
    @Override
    public Fetch remove(List<Integer> ids) {
        return remove(ids, null, null);
    }

    @Override
    public Fetch remove(int id, Func<Download> func, Func<Error> func2) {
        return remove(Collections.singletonList(id), new Func<List<Download>>() {
            @Override
            public void call(List<Download> result) {
                if (result.isEmpty()){
                    if (func2 !=null) {

                        func2.call(Error.REQUEST_DOES_NOT_EXIST);
                    }
                }else {
                    if (func != null) {
                        func.call(result.get(0));
                    }
                }
            }
        }, func2);
    }

    @Override
    public Fetch remove(int id) {

        return remove(id, null, null);
    }

    @Override
    public Fetch removeGroup(int id, Func<List<Download>> func, Func<Error> func2) {
//        return executeRemoveAction({ fetchHandler.removeGroup(id) }, func, func2)
        return executeRemoveAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.removeGroup(id);
            }
        }, func, func2);
    }

    @Override
    public Fetch removeGroup(int id) {
        return removeGroup(id, null, null);
    }

    @Override
    public Fetch removeAll(Func<List<Download>> func, Func<Error> func2) {
        // return executeRemoveAction({ fetchHandler.removeAll() }, func, func2)
        return executeRemoveAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.removeAll();
            }
        }, func, func2);
    }

    @Override
    public Fetch removeAll() {
        return removeAll(null, null);
    }

    @Override
    public Fetch removeAllWithStatus(Status status, Func<List<Download>> func, Func<Error> func2) {
//        return executeRemoveAction({ fetchHandler.removeAllWithStatus(status) }, func, func2);
        return executeRemoveAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.removeAllWithStatus(status);
            }
        }, func, func2);
    }

    @Override
    public Fetch removeAllWithStatus(Status status) {
        return removeAllWithStatus(status, null, null);
    }

    @Override
    public Fetch removeAllInGroupWithStatus(int id, List<Status> statuses, Func<List<Download>> func, Func<Error> func2) {

        return executeRemoveAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.removeAllInGroupWithStatus(id, statuses);
            }
        }, func, func2);
    }

    @Override
    public Fetch removeAllInGroupWithStatus(int id, List<Status> statuses) {
        return removeAllInGroupWithStatus(id, statuses, null, null);
    }

    private Fetch executeDeleteAction(IDownloadAction iDownloadAction, Func<List<Download>> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<Download> downloads = iDownloadAction.invoke();

                        System.out.println("liuluming->" + downloads);
                        for (Download download : downloads) {
                            listenerCoordinator.mainListener.onDeleted(download);
                        }
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {

                                if (func != null) {
                                    func.call(downloads);
                                }
                            }
                        });
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }

    }

    @Override
    public Fetch delete(List<Integer> ids, Func<List<Download>> func, Func<Error> func2) {

        return executeDeleteAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.delete(ids);
            }
        }, func, func2);
    }

    @Override
    public Fetch delete(List<Integer> ids) {
        return delete(ids, null, null);
    }

    @Override
    public Fetch delete(int id, Func<Download> func, Func<Error> func2) {
        return delete(Collections.singletonList(id), new Func<List<Download>>() {
            @Override
            public void call(List<Download> result) {
            if (result.isEmpty()){
                if (func2 !=null) {
                    func2.call(Error.REQUEST_DOES_NOT_EXIST);
                }
            }else {
                if (func != null) {
                    func.call(result.get(0));
                }
            }

            }
        }, func2);

    }

    @Override
    public Fetch delete(int id) {
        return delete(id, null, null);
    }

    @Override
    public Fetch deleteGroup(int id, Func<List<Download>> func, Func<Error> func2) {
        //return executeDeleteAction({ fetchHandler.deleteGroup(id) }, func, func2)
        return executeDeleteAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.deleteGroup(id);
            }
        }, func, func2);
    }

    @Override
    public Fetch deleteGroup(int id) {
        return deleteGroup(id, null, null);
    }

    @Override
    public Fetch deleteAll(Func<List<Download>> func, Func<Error> func2) {
        return executeDeleteAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.deleteAll();
            }
        }, func, func2);
    }

    @Override
    public Fetch deleteAll() {
        return deleteAll(null, null);
    }

    @Override
    public Fetch deleteAllWithStatus(Status status, Func<List<Download>> func, Func<Error> func2) {
        return executeDeleteAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.deleteAllWithStatus(status);
            }
        }, func, func2);
    }

    @Override
    public Fetch deleteAllWithStatus(Status status) {
        return deleteAllWithStatus(status, null, null);
    }

    @Override
    public Fetch deleteAllInGroupWithStatus(int id, List<Status> statuses, Func<List<Download>> func, Func<Error> func2) {
        return executeDeleteAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.deleteAllInGroupWithStatus(id, statuses);
            }
        }, func, func2);
    }

    @Override
    public Fetch deleteAllInGroupWithStatus(int id, List<Status> statuses) {
        return deleteAllInGroupWithStatus(id, statuses, null, null);
    }

    private Fetch executeCancelAction(IDownloadAction downloadAction, Func<List<Download>> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<Download> downloads = downloadAction.invoke();
                        for (Download download : downloads) {
                            listenerCoordinator.mainListener.onCancelled(download);
                        }
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func != null) {

                                    func.call(downloads);
                                }
                            }
                        });
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }
    }


    @Override
    public Fetch cancel(List<Integer> ids, Func<List<Download>> func, Func<Error> func2) {
        return executeCancelAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.cancel(ids);
            }
        }, func, func2);
    }

    @Override
    public Fetch cancel(List<Integer> ids) {
        return cancel(ids, null, null);
    }

    @Override
    public Fetch cancel(int id, Func<Download> func, Func<Error> func2) {

        return cancel(Collections.singletonList(id), new Func<List<Download>>() {
            @Override
            public void call(List<Download> result) {
                if (result.isEmpty()){
                    if (func2 !=null) {

                        func2.call(Error.REQUEST_DOES_NOT_EXIST);
                    }
                }else {
                    if (func != null) {
                        func.call(result.get(0));
                    }
                }
            }
        }, func2);
    }

    @Override
    public Fetch cancel(int id) {
        return cancel(id, null, null);
    }

    @Override
    public Fetch cancelGroup(int id, Func<List<Download>> func, Func<Error> func2) {
        return executeCancelAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.cancelGroup(id);
            }
        }, func, func2);
    }

    @Override
    public Fetch cancelGroup(int id) {
        return cancelGroup(id, null, null);
    }

    @Override
    public Fetch cancelAll(Func<List<Download>> func, Func<Error> func2) {
        return executeCancelAction(new IDownloadAction() {
            @Override
            public List<Download> invoke() {
                return fetchHandler.cancelAll();
            }
        }, func, func2);
    }

    @Override
    public Fetch cancelAll() {
        return cancelAll(null, null);
    }

    @Override
    public Fetch retry(List<Integer> ids, Func<List<Download>> func, Func<Error> func2) {

        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<Download> downloads = fetchHandler.retry(ids);
                        for (Download download : downloads) {
                            listenerCoordinator.mainListener.onQueued(download, false);
                        }
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func !=null){

                                    func.call(downloads);
                                }
                            }
                        });
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }
    }

    @Override
    public Fetch retry(List<Integer> ids) {
        return retry(ids, null, null);
    }

    @Override
    public Fetch retry(int id, Func<Download> func, Func<Error> func2) {

        return retry(Collections.singletonList(id), new Func<List<Download>>() {
            @Override
            public void call(List<Download> result) {
                if (result.isEmpty()){
                    if (func2 !=null) {

                        func2.call(Error.REQUEST_DOES_NOT_EXIST);
                    }
                }else {
                    if (func !=null){
                        func.call(result.get(0));
                    }
                }
            }
        }, func2);
    }

    @Override
    public Fetch retry(int id) {
        return retry(id, null, null);
    }

    @Override
    public Fetch updateRequest(int requestId, Request updatedRequest, boolean notifyListeners, Func<Download> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        Pair<Download, Boolean> downloadPair = fetchHandler.updateRequest(requestId, updatedRequest);
                        Download download = downloadPair.f;
                        if (notifyListeners) {
                            switch (download.getStatus()) {
                                case COMPLETED:
                                    listenerCoordinator.mainListener.onCompleted(download);
                                    break;
                                case FAILED:
                                    listenerCoordinator.mainListener.onError(download, download.getError(), null);
                                    break;
                                case CANCELLED:
                                    listenerCoordinator.mainListener.onCancelled(download);
                                    break;
                                case DELETED:
                                    listenerCoordinator.mainListener.onDeleted(download);
                                    break;
                                case PAUSED:
                                    listenerCoordinator.mainListener.onPaused(download);
                                    break;
                                case QUEUED:
                                    if (!downloadPair.s) {
                                        DownloadInfo downloadCopy = TypeConverterExtensions.toDownloadInfo(download, fetchDatabaseManagerWrapper.getNewDownloadInfoInstance());

                                        downloadCopy.setStatus(Status.ADDED);
                                        listenerCoordinator.mainListener.onAdded(downloadCopy);
                                    }
                                    listenerCoordinator.mainListener.onQueued(download, false);
                                    break;
                                case REMOVED:
                                    listenerCoordinator.mainListener.onRemoved(download);
                                    break;
                                case DOWNLOADING:
                                    break;
                                case ADDED:
                                    listenerCoordinator.mainListener.onAdded(download);
                                    break;
                                case NONE:
                                    break;
                                default:
                                    break;
                            }
                        }
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func !=null){

                                    func.call(download);
                                }
                            }
                        });
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }

    }

    @Override
    public Fetch replaceExtras(int id, Extras extras, Func<Download> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        Download download = fetchHandler.replaceExtras(id, extras);
                        if (func != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func.call(download);
                                }
                            });
                        }
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }
    }

    @Override
    public Fetch resetAutoRetryAttempts(int downloadId, boolean retryDownload, Func2<Download> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        Download download = fetchHandler.resetAutoRetryAttempts(downloadId, retryDownload);
                        if (download != null && download.getStatus() == Status.QUEUED) {
                            listenerCoordinator.mainListener.onQueued(download, false);
                        }
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func !=null){

                                    func.call(download);
                                }
                            }
                        });
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }
    }

    @Override
    public Fetch renameCompletedDownloadFile(int id, String newFileName, Func<Download> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        Download download = fetchHandler.renameCompletedDownloadFile(id, newFileName);
                        if (func != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func.call(download);
                                }
                            });
                        }
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getDownloads(Func<List<Download>> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    List<Download> downloads = fetchHandler.getDownloads();
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){
                                func.call(downloads);
                            }
                        }
                    });

                }
            });
            return this;
        }
    }

    @Override
    public Fetch getDownloads(List<Integer> idList, Func<List<Download>> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    List<Download> downloads = fetchHandler.getDownloads(idList);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(downloads);
                            }
                        }
                    });
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getDownload(int id, Func2<Download> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    Download download = fetchHandler.getDownload(id);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func2 !=null){

                                func2.call(download);
                            }
                        }
                    });
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getDownloadsInGroup(int groupId, Func<List<Download>> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    List<Download> downloads = fetchHandler.getDownloadsInGroup(groupId);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(downloads);
                            }
                        }
                    });
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getDownloadsWithStatus(Status status, Func<List<Download>> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    List<Download> downloads = fetchHandler.getDownloadsWithStatus(status);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(downloads);
                            }
                        }
                    });
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getDownloadsWithStatus(List<Status> statuses, Func<List<Download>> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    List<Download> downloads = fetchHandler.getDownloadsWithStatus(statuses);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(downloads);
                            }
                        }
                    });
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getDownloadsByRequestIdentifier(Long identifier, Func<List<Download>> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    List<Download> downloads = fetchHandler.getDownloadsByRequestIdentifier(identifier);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(downloads);
                            }
                        }
                    });
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getDownloadsInGroupWithStatus(int groupId, List<Status> statuses, Func<List<Download>> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    List<Download> downloads = fetchHandler.getDownloadsInGroupWithStatus(groupId, statuses);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(downloads);
                            }
                        }
                    });
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getDownloadsByTag(String tag, Func<List<Download>> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    List<Download> downloads = fetchHandler.getDownloadsByTag(tag);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(downloads);
                            }
                        }
                    });
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getFetchGroup(int group, Func<FetchGroup> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    FetchGroup fetchGroup = fetchHandler.getFetchGroup(group);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(fetchGroup);
                            }
                        }
                    });
                }
            });
        }
        return this;
    }

    @Override
    public Fetch getAllGroupIds(Func<List<Integer>> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    List<Integer> groupIdList = fetchHandler.getAllGroupIds();
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(groupIdList);
                            }
                        }
                    });
                }
            });
            return this;
        }
    }

    @Override
    public Fetch addListener(FetchListener listener, boolean notify) {
        return addListener(listener, notify, Defaults.DEFAULT_ENABLE_LISTENER_AUTOSTART_ON_ATTACHED);
    }

    @Override
    public Fetch addListener(FetchListener listener, boolean notify, boolean autoStart) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    fetchHandler.addListener(listener, notify, autoStart);
                }
            });

            return this;
        }
    }

    @Override
    public Fetch removeListener(FetchListener listener) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    fetchHandler.removeListener(listener);
                }
            });
            return this;
        }
    }

    @Override
    public Fetch addCompletedDownload(CompletedDownload completedDownload, boolean alertListeners, Func<Download> func, Func<Error> func2) {
        return addCompletedDownloads(Collections.singletonList(completedDownload), alertListeners, new Func<List<Download>>() {
            @Override
            public void call(List<Download> result) {
                if (result.isEmpty()){
                    if (func2 !=null) {
                        func2.call(Error.COMPLETED_NOT_ADDED_SUCCESSFULLY);
                    }
                }else {
                    if (func !=null) {
                        func.call(result.get(0));
                    }
                }
            }
        }, func2);

    }

    @Override
    public Fetch addCompletedDownloads(List<CompletedDownload> completedDownloads, boolean alertListeners, Func<List<Download>> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {

                    try {
                        List<Download> downloads = fetchHandler.enqueueCompletedDownloads(completedDownloads);
                        if (alertListeners) {
                            for (Download download : downloads) {
                                listenerCoordinator.mainListener.onCompleted(download);
                            }
                        }
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func !=null){

                                    func.call(downloads);
                                }
                            }
                        });

                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    if (func2 !=null){

                                        func2.call(error);
                                    }
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getDownloadBlocks(int downloadId, Func<List<DownloadBlock>> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    List<DownloadBlock> downloadBlocksList = fetchHandler.getDownloadBlocks(downloadId);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(downloadBlocksList);
                            }
                        }
                    });
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getContentLengthForRequest(Request request, boolean fromServer, Func<Long> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.executeWorkerTask(new Runnable() {
                @Override
                public void run() {
                    try {
                        Long contentLength = fetchHandler.getContentLengthForRequest(request, fromServer);
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func !=null){

                                    func.call(contentLength);
                                }
                            }
                        });
                    } catch (Exception e) {
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getContentLengthForRequests(List<Request> requests, boolean fromServer, Func<List<Pair<Request, Long>>> func, Func<List<Pair<Request, Error>>> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.executeWorkerTask(new Runnable() {
                @Override
                public void run() {

                    List<Pair<Request, Long>> results = new ArrayList<>();
                    List<Pair<Request, Error>> results2 = new ArrayList<>();
                    for (Request request : requests) {
                        try {
                            results.add(new Pair(request, fetchHandler.getContentLengthForRequest(request, fromServer)));
                        } catch (Exception e) {
                            logger.e("Fetch with namespace $namespace error", e);
                            Error error = getErrorFromMessage(e.getMessage());
                            error.setThrowable(e);
                            results2.add(new Pair(request, error));
                        }
                    }
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(results);
                            }
                        }
                    });
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func2 !=null){

                                func2.call(results2);
                            }
                        }
                    });

                }
            });
            return this;
        }
    }

    @Override
    public Fetch getServerResponse(String url, Map<String, String> headers, Func<Downloader.Response> func, Func<Error> func2) {
        synchronized (lock) {
            System.out.println("llm=================="+url+"====1===");
            throwExceptionIfClosed();
            handlerWrapper.executeWorkerTask(new Runnable() {
                @Override
                public void run() {
                    try {
                        Downloader.Response response = fetchHandler.getServerResponse(url, headers);
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func !=null){

                                    func.call(response);
                                }
                            }
                        });
                    } catch (Exception e) {
                        logger.e("Fetch with namespace $namespace error", e);
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }
    }

    @Override
    public Fetch getFetchFileServerCatalog(Request request, Func<List<FileResource>> func, Func<Error> func2) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.executeWorkerTask(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<FileResource> fileResourceList = fetchHandler.getFetchFileServerCatalog(request);
                        uiHandler.postTask(new Runnable() {
                            @Override
                            public void run() {
                                if (func !=null){

                                    func.call(fileResourceList);
                                }
                            }
                        });
                    } catch (Exception e) {
                        logger.e("Fetch with namespace $namespace error", e);
                        Error error = getErrorFromMessage(e.getMessage());
                        error.setThrowable(e);
                        if (func2 != null) {
                            uiHandler.postTask(new Runnable() {
                                @Override
                                public void run() {
                                    func2.call(error);
                                }
                            });
                        }
                    }
                }
            });
            return this;
        }
    }

    @Override
    public Fetch enableLogging(Boolean enabled) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    fetchHandler.enableLogging(enabled);
                }
            });
            return this;
        }
    }

    @Override
    public Fetch setGlobalNetworkType(NetworkType networkType) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    fetchHandler.setGlobalNetworkType(networkType);
                }
            });
            return this;
        }
    }

    @Override
    public Fetch setDownloadConcurrentLimit(int downloadConcurrentLimit) {
        synchronized (lock) {
            throwExceptionIfClosed();
            if (downloadConcurrentLimit < 0) {
                throw new FetchException("Concurrent limit cannot be less than 0");
            }
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    fetchHandler.setDownloadConcurrentLimit(downloadConcurrentLimit);
                }
            });
            return this;
        }
    }

    @Override
    public void close() {
        synchronized (lock) {
            if (closed) {
                return;
            }
            closed = true;
            logger.d("$namespace closing/shutting down");
            handlerWrapper.removeCallbacks(activeDownloadsRunnable);
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    try {
                        fetchHandler.close();
                    } catch (Exception e) {
                        logger.e("exception occurred whiles shutting down Fetch with namespace:$namespace", e);
                    }
                }
            });
        }
    }

    @Override
    public Set<FetchListener> getListenerSet() {
        synchronized (lock) {
            throwExceptionIfClosed();
            return fetchHandler.getListenerSet();
        }
    }

    @Override
    public void awaitFinishOrTimeout(Long allowTimeInMilliseconds) {
        FetchUtils.awaitFinishOrTimeout(allowTimeInMilliseconds, fetchHandler);
    }

    @Override
    public void awaitFinish() {
        awaitFinishOrTimeout((long) -1);
    }

    @Override
    public Fetch attachFetchObserversForDownload(int downloadId, FetchObserver<Download>... fetchObservers) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {

                    fetchHandler.addFetchObserversForDownload(downloadId, fetchObservers);
                }
            });
            return this;
        }
    }

    @Override
    public Fetch removeFetchObserversForDownload(int downloadId, FetchObserver<Download>... fetchObservers) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    fetchHandler.removeFetchObserversForDownload(downloadId, fetchObservers);
                }
            });
            return this;
        }
    }

    @Override
    public Fetch hasActiveDownloads(boolean includeAddedDownloads, Func<Boolean> func) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    Boolean hasActiveDownloads = fetchHandler.hasActiveDownloads(includeAddedDownloads);
                    uiHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            if (func !=null){

                                func.call(hasActiveDownloads);
                            }
                        }
                    });
                }
            });
        }
        return this;
    }

    @Override
    public Fetch addActiveDownloadsObserver(boolean includeAddedDownloads, FetchObserver<Boolean> fetchObservers) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    activeDownloadsSet.add(new ActiveDownloadInfo(fetchObservers, includeAddedDownloads));
                }
            });
            return this;
        }
    }

    @Override
    public Fetch removeActiveDownloadsObserver(FetchObserver<Boolean> fetchObservers) {
        synchronized (lock) {
            throwExceptionIfClosed();
            handlerWrapper.post(new Runnable() {
                @Override
                public void run() {
                    Iterator<ActiveDownloadInfo> iterator = activeDownloadsSet.iterator();
                    while (iterator.hasNext()) {
                        ActiveDownloadInfo activeDownloadInfo = iterator.next();
                        if (activeDownloadInfo.getFetchObserver() == fetchObservers) {
                            iterator.remove();
                            logger.d("Removed ActiveDownload FetchObserver $fetchObserver");
                            break;
                        }
                    }
                }
            });
            return this;
        }
    }

    public static FetchImpl newInstance(FetchModulesBuilder.Modules modules) {
        return new FetchImpl(
            modules.getFetchConfiguration().getNamespace(),
            modules.getFetchConfiguration(),
            modules.getHandlerWrapper(),
            modules.getUiHandler(),
            modules.fetchHandler,
            modules.getFetchConfiguration().getLogger(),
            modules.getListenerCoordinator(),
            modules.getFetchDatabaseManagerWrapper());
    }
}
